/*********************************************************************
*                    SEGGER Microcontroller GmbH                     *
*                        The Embedded Experts                        *
**********************************************************************
*                                                                    *
*            (c) 1995 - 2021 SEGGER Microcontroller GmbH             *
*                                                                    *
*       www.segger.com     Support: support@segger.com               *
*                                                                    *
**********************************************************************
*                                                                    *
*       SEGGER SystemView * Real-time application analysis           *
*                                                                    *
**********************************************************************
*                                                                    *
* All rights reserved.                                               *
*                                                                    *
* SEGGER strongly recommends to not make any changes                 *
* to or modify the source code of this software in order to stay     *
* compatible with the SystemView and RTT protocol, and J-Link.       *
*                                                                    *
* Redistribution and use in source and binary forms, with or         *
* without modification, are permitted provided that the following    *
* condition is met:                                                  *
*                                                                    *
* o Redistributions of source code must retain the above copyright   *
*   notice, this condition and the following disclaimer.             *
*                                                                    *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND             *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,        *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF           *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE           *
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR           *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;    *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF      *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT          *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE  *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH   *
* DAMAGE.                                                            *
*                                                                    *
**********************************************************************
*                                                                    *
*       SystemView version: 3.30                                    *
*                                                                    *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------

File    : SEGGER_SYSVIEW_FreeRTOS.c
Purpose : Interface between FreeRTOS and SystemView.
Revision: $Rev: 7947 $
*/
#include "FreeRTOS.h"
#include "task.h"
#include "SEGGER_SYSVIEW.h"
#include "SEGGER_SYSVIEW_FreeRTOS.h"
#include "string.h" // Required for memset

typedef struct SYSVIEW_FREERTOS_TASK_STATUS SYSVIEW_FREERTOS_TASK_STATUS;

struct SYSVIEW_FREERTOS_TASK_STATUS {
    U32 xHandle;
    const char *pcTaskName;
    unsigned uxCurrentPriority;
    U32 pxStack;
    unsigned uStackHighWaterMark;
};

static SYSVIEW_FREERTOS_TASK_STATUS _aTasks[SYSVIEW_FREERTOS_MAX_NOF_TASKS];
static unsigned _NumTasks;

/*********************************************************************
 *
 *       _cbSendTaskList()
 *
 *  Function description
 *    This function is part of the link between FreeRTOS and SYSVIEW.
 *    Called from SystemView when asked by the host, it uses SYSVIEW
 *    functions to send the entire task list to the host.
 */
static void _cbSendTaskList(void) {
    unsigned n;

    for (n = 0; n < _NumTasks; n++) {
#if INCLUDE_uxTaskGetStackHighWaterMark // Report Task Stack High Watermark
        _aTasks[n].uStackHighWaterMark = uxTaskGetStackHighWaterMark((TaskHandle_t)_aTasks[n].xHandle);
#endif
        SYSVIEW_SendTaskInfo((U32)_aTasks[n].xHandle, _aTasks[n].pcTaskName, (unsigned)_aTasks[n].uxCurrentPriority, (U32)_aTasks[n].pxStack, (unsigned)_aTasks[n].uStackHighWaterMark);
    }
}

/*********************************************************************
 *
 *       _cbGetTime()
 *
 *  Function description
 *    This function is part of the link between FreeRTOS and SYSVIEW.
 *    Called from SystemView when asked by the host, returns the
 *    current system time in micro seconds.
 */
static U64 _cbGetTime(void) {
    U64 Time;

    Time = xTaskGetTickCountFromISR();
    Time *= portTICK_PERIOD_MS;
    Time *= 1000;
    return Time;
}

/*********************************************************************
 *
 *       Global functions
 *
 **********************************************************************
 */
/*********************************************************************
 *
 *       SYSVIEW_AddTask()
 *
 *  Function description
 *    Add a task to the internal list and record its information.
 */
void SYSVIEW_AddTask(U32 xHandle, const char *pcTaskName, unsigned uxCurrentPriority, U32 pxStack, unsigned uStackHighWaterMark) {

    if (memcmp(pcTaskName, "IDLE", 5) == 0) {
        return;
    }

    if (_NumTasks >= SYSVIEW_FREERTOS_MAX_NOF_TASKS) {
        SEGGER_SYSVIEW_Warn("SYSTEMVIEW: Could not record task information. Maximum number of tasks reached.");
        return;
    }

    _aTasks[_NumTasks].xHandle = xHandle;
    _aTasks[_NumTasks].pcTaskName = pcTaskName;
    _aTasks[_NumTasks].uxCurrentPriority = uxCurrentPriority;
    _aTasks[_NumTasks].pxStack = pxStack;
    _aTasks[_NumTasks].uStackHighWaterMark = uStackHighWaterMark;

    _NumTasks++;

    SYSVIEW_SendTaskInfo(xHandle, pcTaskName, uxCurrentPriority, pxStack, uStackHighWaterMark);
}

/*********************************************************************
 *
 *       SYSVIEW_UpdateTask()
 *
 *  Function description
 *    Update a task in the internal list and record its information.
 */
void SYSVIEW_UpdateTask(U32 xHandle, const char *pcTaskName, unsigned uxCurrentPriority, U32 pxStack, unsigned uStackHighWaterMark) {
    unsigned n;

    if (memcmp(pcTaskName, "IDLE", 5) == 0) {
        return;
    }

    for (n = 0; n < _NumTasks; n++) {
        if (_aTasks[n].xHandle == xHandle) {
            break;
        }
    }
    if (n < _NumTasks) {
        _aTasks[n].pcTaskName = pcTaskName;
        _aTasks[n].uxCurrentPriority = uxCurrentPriority;
        _aTasks[n].pxStack = pxStack;
        _aTasks[n].uStackHighWaterMark = uStackHighWaterMark;

        SYSVIEW_SendTaskInfo(xHandle, pcTaskName, uxCurrentPriority, pxStack, uStackHighWaterMark);
    } else {
        SYSVIEW_AddTask(xHandle, pcTaskName, uxCurrentPriority, pxStack, uStackHighWaterMark);
    }
}

/*********************************************************************
 *
 *       SYSVIEW_DeleteTask()
 *
 *  Function description
 *    Delete a task from the internal list.
 */
void SYSVIEW_DeleteTask(U32 xHandle) {
    unsigned n;

    if (_NumTasks == 0) {
        return; // Early out
    }
    for (n = 0; n < _NumTasks; n++) {
        if (_aTasks[n].xHandle == xHandle) {
            break;
        }
    }
    if (n == (_NumTasks - 1)) {
        //
        // Task is last item in list.
        // Simply zero the item and decrement number of tasks.
        //
        memset(&_aTasks[n], 0, sizeof(_aTasks[n]));
        _NumTasks--;
    } else if (n < _NumTasks) {
        //
        // Task is in the middle of the list.
        // Move last item to current position and decrement number of tasks.
        // Order of tasks does not really matter, so no need to move all following items.
        //
        _aTasks[n].xHandle = _aTasks[_NumTasks - 1].xHandle;
        _aTasks[n].pcTaskName = _aTasks[_NumTasks - 1].pcTaskName;
        _aTasks[n].uxCurrentPriority = _aTasks[_NumTasks - 1].uxCurrentPriority;
        _aTasks[n].pxStack = _aTasks[_NumTasks - 1].pxStack;
        _aTasks[n].uStackHighWaterMark = _aTasks[_NumTasks - 1].uStackHighWaterMark;
        memset(&_aTasks[_NumTasks - 1], 0, sizeof(_aTasks[_NumTasks - 1]));
        _NumTasks--;
    }
}

/*********************************************************************
 *
 *       SYSVIEW_SendTaskInfo()
 *
 *  Function description
 *    Record task information.
 */
void SYSVIEW_SendTaskInfo(U32 TaskID, const char *sName, unsigned Prio, U32 StackBase, unsigned StackSize) {
    SEGGER_SYSVIEW_TASKINFO TaskInfo;

    memset(&TaskInfo, 0, sizeof(TaskInfo)); // Fill all elements with 0 to allow extending the structure in future version without breaking the code
    TaskInfo.TaskID = TaskID;
    TaskInfo.sName = sName;
    TaskInfo.Prio = Prio;
    TaskInfo.StackBase = StackBase;
    TaskInfo.StackSize = StackSize;
    SEGGER_SYSVIEW_SendTaskInfo(&TaskInfo);
}

/*********************************************************************
 *
 *       SYSVIEW_RecordU32x4()
 *
 *  Function description
 *    Record an event with 4 parameters
 */
void SYSVIEW_RecordU32x4(unsigned Id, U32 Para0, U32 Para1, U32 Para2, U32 Para3) {
    U8 aPacket[SEGGER_SYSVIEW_INFO_SIZE + 4 * SEGGER_SYSVIEW_QUANTA_U32];
    U8 *pPayload;
    //
    pPayload = SEGGER_SYSVIEW_PREPARE_PACKET(aPacket); // Prepare the packet for SystemView
    pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para0); // Add the first parameter to the packet
    pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para1); // Add the second parameter to the packet
    pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para2); // Add the third parameter to the packet
    pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para3); // Add the fourth parameter to the packet
    //
    SEGGER_SYSVIEW_SendPacket(&aPacket[0], pPayload, Id); // Send the packet
}

/*********************************************************************
 *
 *       SYSVIEW_RecordU32x5()
 *
 *  Function description
 *    Record an event with 5 parameters
 */
void SYSVIEW_RecordU32x5(unsigned Id, U32 Para0, U32 Para1, U32 Para2, U32 Para3, U32 Para4) {
    U8 aPacket[SEGGER_SYSVIEW_INFO_SIZE + 5 * SEGGER_SYSVIEW_QUANTA_U32];
    U8 *pPayload;
    //
    pPayload = SEGGER_SYSVIEW_PREPARE_PACKET(aPacket); // Prepare the packet for SystemView
    pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para0); // Add the first parameter to the packet
    pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para1); // Add the second parameter to the packet
    pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para2); // Add the third parameter to the packet
    pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para3); // Add the fourth parameter to the packet
    pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, Para4); // Add the fifth parameter to the packet
    //
    SEGGER_SYSVIEW_SendPacket(&aPacket[0], pPayload, Id); // Send the packet
}

/*********************************************************************
 *
 *       Public API structures
 *
 **********************************************************************
 */
// Callbacks provided to SYSTEMVIEW by FreeRTOS
extern "C" const SEGGER_SYSVIEW_OS_API SYSVIEW_X_OS_TraceAPI = {
    _cbGetTime,
    _cbSendTaskList,
};

/*************************** End of file ****************************/
